﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Windows;
using Microsoft.Phone.Controls;
using System.Windows.Media.Imaging;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using System.Device.Location;

namespace geopg_wp7
{
    public partial class UploadPage : PhoneApplicationPage
    {
        public Dictionary<string, object> parameters;
        string boundary = "----------" + DateTime.Now.Ticks.ToString();
        public byte[] ba;

        // URI for Google Maps reverse-geocoding
        public string baseUri = "http://maps.googleapis.com/maps/api/geocode/xml?latlng={0},{1}&sensor=false";
        public string baseUri2 = "http://maps.googleapis.com/maps/api/geocode/xml?address={0}&sensor=false";
        public GeoCoordinate coord;
        GeoCoordinateWatcher g;
        Double longitude;
        Double latitude;

        // Constructor
        public UploadPage()
        {
            InitializeComponent();
            // Create Location-Service
            g = new GeoCoordinateWatcher();
            // Retry starting Service each second
            g.TryStart(false, TimeSpan.FromMilliseconds(1000));
            // Set global variable
            GlobalVariables.g = g;
            // Get current location
            coord = g.Position.Location;
            longitude = coord.Longitude;
            latitude = coord.Latitude;
            LongBox.Text = longitude.ToString(System.Globalization.CultureInfo.InvariantCulture);
            LatBox.Text = latitude.ToString(System.Globalization.CultureInfo.InvariantCulture);
            if (GlobalVariables.longitude == null)
                GlobalVariables.longitude = LongBox.Text;
            if (GlobalVariables.latitude == null)
                GlobalVariables.latitude = LatBox.Text;
            // Retrieve Address from Longitude and Latitude via Google Maps
            RetrieveFormatedAddress(GlobalVariables.latitude, GlobalVariables.longitude);
        }

        protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);
            titleBox.IsEnabled = false;
            descBox.IsEnabled = false;
            LatBox.IsEnabled = false;
            LongBox.IsEnabled = false;
            AddBox.IsEnabled = false;
            addButton.IsEnabled = false;
            if (this.NavigationContext.QueryString.ContainsKey("title"))
            {
                titleBox.Text = this.NavigationContext.QueryString["title"];
                GlobalVariables.title = titleBox.Text;
            }
            else
                titleBox.Text = GlobalVariables.title;

            if (this.NavigationContext.QueryString.ContainsKey("desc"))
            {
                descBox.Text = this.NavigationContext.QueryString["desc"];
                GlobalVariables.desc = descBox.Text;
            }
            else
                descBox.Text = GlobalVariables.desc;

            if (this.NavigationContext.QueryString.ContainsKey("latitude"))
            {
                LatBox.Text = this.NavigationContext.QueryString["latitude"];
                GlobalVariables.latitude = LatBox.Text;
            }
            else
                LatBox.Text = GlobalVariables.latitude;

            if (this.NavigationContext.QueryString.ContainsKey("longitude"))
            {
                LongBox.Text = this.NavigationContext.QueryString["longitude"];
                GlobalVariables.longitude = LongBox.Text;
            }
            else
                LongBox.Text = GlobalVariables.longitude;

        }

        private void editButton_Click(object sender, RoutedEventArgs e)
        {
            // Makes the TextBoxes editable, if wanted
            titleBox.IsEnabled = true;
            descBox.IsEnabled = true;
            AddBox.IsEnabled = true;
            addButton.IsEnabled = true;
            //titleBox.Text = this.NavigationContext.QueryString[""];
            //descBox.Text = this.NavigationContext.QueryString[""];
        }


        private void uploadButton_Click(object sender, RoutedEventArgs e)
        {
                titleBox.IsEnabled = false;
                descBox.IsEnabled = false;
                AddBox.IsEnabled = false;
                addButton.IsEnabled = false;
                uploadButton.IsEnabled = false;
                editButton.IsEnabled = false;
                try
                {
                    BitmapImage image = new BitmapImage();
                    image.SetSource(GlobalVariables.photores.ChosenPhoto);
                    //file.Source = image;
                    using (MemoryStream ms = new MemoryStream())
                    {
                        WriteableBitmap btmMap = new WriteableBitmap(image);
                        // write an image into the stream
                        System.Windows.Media.Imaging.Extensions.SaveJpeg(btmMap, ms, image.PixelWidth, image.PixelHeight, 0, 100);
                        ba = ms.ToArray();
                    }
                }
                catch (ArgumentNullException) { /* Nothing */ }
                parameters = new Dictionary<string, object>()
            {
                {"title", titleBox.Text},
                {"desc", descBox.Text},
                {"file", ba},
                {"latitude", latitude.ToString()},
                {"longitude", longitude.ToString()}
            };
                // Prepare web request...
                Uri address = new Uri("http://geopg.de/backend/PictureUpload");
                HttpWebRequest request = WebRequest.Create(address) as HttpWebRequest;
                request.Headers["Cookie"] = GlobalVariables.cookie;
                request.Method = "POST";
                request.ContentType = string.Format("multipart/form-data; boundary={0}", boundary);
                request.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), request);
        }

        private void GetRequestStreamCallback(IAsyncResult asynchronousResult)
        {
            this.Dispatcher.BeginInvoke(delegate()
            {
                // Start ProgressBar
                uploadProgress.Visibility = Visibility.Visible;
                HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
                Stream postStream = request.EndGetRequestStream(asynchronousResult);
                writeMultipartObject(postStream, parameters);
                postStream.Close();
                request.BeginGetResponse(new AsyncCallback(GetResponseCallback), request);
            });
        }

        private void GetResponseCallback(IAsyncResult asynchronousResult)
        {
            try
            {
                HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
                HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
                Stream streamResponse = response.GetResponseStream();
                StreamReader streamRead = new StreamReader(streamResponse);
                string responseString = streamRead.ReadToEnd();
                this.Dispatcher.BeginInvoke(delegate()
                {
                    uploadProgress.Visibility = Visibility.Collapsed;
                    // Output is the position of the string. If it doesn't exist, its position is "-1"
                    if (responseString.IndexOf("cause") != -1)
                    {
                        if (responseString.IndexOf("cause=\"0\"") != -1)
                            MessageBox.Show("Internal Error (SQL)");
                        else if (responseString.IndexOf("cause=\"1\"") != -1)
                            MessageBox.Show("Expected POST but got GET request.");
                        else if (responseString.IndexOf("cause=\"13\"") != -1)
                            MessageBox.Show("Please enter a title.");
                        else if (responseString.IndexOf("cause=\"14\"") != -1)
                            MessageBox.Show("Please enter a description.");
                        else if (responseString.IndexOf("cause=\"16\"") != -1)
                            MessageBox.Show("No file found.");
                        else if (responseString.IndexOf("cause=\"15\"") != -1)
                            MessageBox.Show("Unsupported format.");
                        else if (responseString.IndexOf("cause=\"23\"") != -1)
                            MessageBox.Show("Filesize too big.");
                        else if (responseString.IndexOf("cause=\"32\"") != -1)
                            MessageBox.Show("Missing location-data.");
                        else if (responseString.IndexOf("cause=\"33\"") != -1)
                            MessageBox.Show("Unsupported Longitude-Format");
                        else if (responseString.IndexOf("cause=\"34\"") != -1)
                            MessageBox.Show("Unsupported Latitude-Format");
                        else
                            MessageBox.Show("An unexpected error occured.");

                        // Enable GUI for editing
                        titleBox.IsEnabled = true;
                        descBox.IsEnabled = true;
                        AddBox.IsEnabled = true;
                        addButton.IsEnabled = true;
                        uploadButton.IsEnabled = true;
                        editButton.IsEnabled = true;
                    }
                    else
                    {
                        MessageBox.Show("Upload complete. \n" + responseString);
                        // delete all ressources from the GeoCoordinateWatcher (to be able to retrieve new location data)
                        g.Dispose();
                        NavigationService.Navigate(new Uri("//MainPage.xaml", UriKind.Relative));
                    }
                });
                streamResponse.Close();
                streamRead.Close();
                // Release the HttpWebResponse
                response.Close();
            }
            catch (WebException e)
            {
                this.Dispatcher.BeginInvoke(delegate()
                {
                    MessageBox.Show("Can't establish connection.");
                });
            }
        }


        public void writeMultipartObject(Stream stream, object data)
        {
            StreamWriter writer = new StreamWriter(stream);
            if (data != null)
            {
                foreach (var entry in data as Dictionary<string, object>)
                {
                    WriteEntry(writer, entry.Key, entry.Value);
                }
            }
            writer.Write("--");
            writer.Write(boundary);
            writer.WriteLine("--");
            writer.Flush();
        }

        private void WriteEntry(StreamWriter writer, string key, object value)
        {
            if (value != null)
            {
                writer.Write("--");
                writer.WriteLine(boundary);
                if (value is byte[])
                {
                    byte[] ba = value as byte[];

                    writer.WriteLine(@"Content-Disposition: form-data; name=""{0}""; filename=""{1}""", key, "sentPhoto.jpg");
                    //writer.WriteLine(@"Content-Type: application/octet-stream");
                    writer.WriteLine(@"Content-Type: image/jpeg");
                    writer.WriteLine(@"Content-Length: " + ba.Length);
                    writer.WriteLine();
                    writer.Flush();
                    Stream output = writer.BaseStream;

                    output.Write(ba, 0, ba.Length);
                    output.Flush();
                    writer.WriteLine();
                }
                else
                {
                    writer.WriteLine(@"Content-Disposition: form-data; name=""{0}""", key);
                    writer.WriteLine();
                    writer.WriteLine(value.ToString());
                }
            }
        }

        void RetrieveFormatedLatLng(string add)
        {
            string requestUri2 = string.Format(baseUri2, add);

            WebClient wc2 = new WebClient();
            wc2.DownloadStringCompleted += new DownloadStringCompletedEventHandler(wc_DownloadStringCompletedLatLng);
            wc2.DownloadStringAsync(new Uri(requestUri2));
        }

        // need to add own wc_DownloadStringCompleted for output of <lat> and <lng> of XML-File
        void RetrieveFormatedAddress(string lat, string lng)
        {
            string requestUri = string.Format(baseUri, lat, lng);

            WebClient wc = new WebClient();
            wc.DownloadStringCompleted += new DownloadStringCompletedEventHandler(wc_DownloadStringCompletedAdd);
            wc.DownloadStringAsync(new Uri(requestUri));
        }

        void wc_DownloadStringCompletedLatLng(object sender, DownloadStringCompletedEventArgs e)
        {
            var xmlElm = XElement.Parse(e.Result);

            var status = (from elm in xmlElm.Descendants()
                          where elm.Name == "status"
                          select elm).FirstOrDefault();
            //MessageBox.Show(status.ToString());
            if (status.Value.ToLower() == "ok")
            {
                var res1 = (from elm in xmlElm.Descendants()
                            where elm.Name == "lat"
                            select elm).FirstOrDefault();
                var res2 = (from elm in xmlElm.Descendants()
                            where elm.Name == "lng"
                            select elm).FirstOrDefault();
                LatBox.Text = res1.Value;
                LongBox.Text = res2.Value;
                // Needs to be changed, when we take own variables
                //MessageBox.Show("Geocode for: '1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA' is \n" + "Latitude: " + res1.Value + " - Longitude: " + res2.Value);
            }
            else
            {
                LatBox.Text = "Unknown";
                LongBox.Text = "Unknown";
                MessageBox.Show("Please enter a valid address-format: \n STREET NUMBER, CITY ZIP-CODE (, COUNTRY)");
            }
        }

        void wc_DownloadStringCompletedAdd(object sender, DownloadStringCompletedEventArgs e2)
        {
            var xmlElm2 = XElement.Parse(e2.Result);

            var status2 = (from elm in xmlElm2.Descendants()
                           where elm.Name == "status"
                           select elm).FirstOrDefault();
            //MessageBox.Show(status.ToString());
            if (status2.Value.ToLower() == "ok")
            {
                var res2 = (from elm in xmlElm2.Descendants()
                            where elm.Name == "formatted_address"
                            select elm).FirstOrDefault();
                AddBox.Text = res2.Value;
            }
            else
            {
                AddBox.Text = "No Address Found";
            }
        }

        public void mapButton_Click(object sender, EventArgs e)
        {
            // TO DO: Send location (long/lat) to MapPage via navigation-override and use it in map1.setView(location, zoomlevel)
            NavigationService.Navigate(new Uri("//MapPage.xaml?latitude=" + LatBox.Text + "&longitude=" + LongBox.Text, UriKind.Relative));
        }

        public void addButton_Click(object sender, EventArgs e)
        {
            RetrieveFormatedLatLng(AddBox.Text);
        }
         
        private void mainPage_Click(object sender, EventArgs e)
        {
            NavigationService.Navigate(new Uri("//MainPage.xaml", UriKind.Relative));
        }

        private void localPictures_Click(object sender, EventArgs e)
        {
            NavigationService.Navigate(new Uri("//LocalPicturesPage.xaml", UriKind.Relative));
        }

        private void settings_Click(object sender, EventArgs e)
        {
            NavigationService.Navigate(new Uri("//SettingsPage.xaml", UriKind.Relative));
        }

        private void info_Click(object sender, EventArgs e)
        {
            NavigationService.Navigate(new Uri("//InfoPage.xaml", UriKind.Relative));
        }

        private void login_Click(object sender, EventArgs e)
        {
            NavigationService.Navigate(new Uri("//LoginPage.xaml", UriKind.Relative));
        }
    }
}